import time
from functools import wraps


# 传统意义修改函数,但是会修改到源代码
# def func():
#     start_time = time.time()
#     print("Hello World!")
#     time.sleep(1)
#     end_time = time.time()
#     total_time = end_time - start_time
#     print("总计用时:", "{:.2f}s".format(total_time))
#
#
# func()

#  在不修改函数的前提下，再定义一个函数,但是会改变调用方式
# def demo(func):
#     start_time = time.time()
#     func()
#     end_time = time.time()
#     total_time = end_time - start_time
#     print("总计用时:", "{:.2f}s".format(total_time))
#
#
# def f():
#     print("Hello World!")
#     time.sleep(1)

# 使用装饰器函数，不改变原有的fun()调用函数的方式，用到的是闭包
# def demo(func):
#     # @wraps 接受一个函数来进行装饰，并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
#     @wraps(func)
#     def wrapper():
#         start_time = time.time()
#         func()
#         end_time = time.time()
#         total_time = end_time - start_time
#         print("总计用时:", "{:.2f}s".format(total_time))
#     return wrapper
#
#
# @demo
# def fun():
#     print("Hello World!")
#     time.sleep(1)

#  定义普通的装饰器
# def demo(func):
#     def wrapper(*args, **kwargs):
#         start_time = time.time()
#         func(*args, **kwargs)
#         time.sleep(1)
#         end_time = time.time()
#         total_time = end_time - start_time
#         print("总计用时:", "{:.2f}s".format(total_time))
#     return wrapper
#
#
# @demo
# def fun_1(a, b):
#     print("hello fun_1")
#     print("result is {}".format(a + b))
#
#
# def fun_2(a, b, c):
#     print("hello fun_2")
#     print("result is {}".format(a + b + c))

#  定义带参数装饰器,装饰器允许传入参数，一个携带了参数的装饰器将有三层函数。
# def time_sleep(num):
#     def demo(func):
#         def wrapper(*args, **kwargs):
#             start_time = time.time()
#             func(*args, **kwargs)
#             print("延时{}s".format(num))
#             time.sleep(num)
#             end_time = time.time()
#             total_time = end_time - start_time
#             print("time is {:.2f}s".format(total_time))
#         return wrapper
#     return demo
#
#
# @time_sleep(3)
# def fun(a, b, c):
#     print("hello fun, 开始调用fun函数")
#     print("result is {}".format((a+b+c)))

#  对有返回值的函数进行装饰
# def demo(func):
#     def wrapper(*args, **kwargs):
#         start_time = time.time()
#         fun_value = func(*args, **kwargs)
#         end_time = time.time()
#         total_time = end_time - start_time
#         print("time is {:2f}s".format(total_time))
#         return fun_value
#     return wrapper
#
#
# @demo
# def fun(a, b):
#     print("hello fun,调用fun函数")
#     time.sleep(1)
#     return a + b

#  定义多个装饰器，装饰一个函数,装饰器是可叠加使用，
#  对于Python中的“@语法”，装饰器的调用顺序与使用“@语法”声明的执行顺序相反。
#  例如：上述例子中函数执行关系应该表示为  “fun(3,4) = demo_1(demo_2(fun(3,4)))”。

# def demo_1(func):
#     print("demo_1 is in ")
#
#     def wrapper(*args, **kwargs):
#         print("this is demo_1 wrapper")
#         start_time = time.time()
#         func(*args, **kwargs)
#         end_time = time.time()
#         total_time = end_time - start_time
#         print("time is {:.2f} s".format(total_time))
#         print("demo_1_wrapper is over")
#
#     print("demo_1 is out")
#     return wrapper
#
#
# def demo_2(func):
#     print("demo_2 is in ")
#
#     def wrapper(*args, **kwargs):
#         print("this is demo_2 wrapper")
#         func(*args, **kwargs)
#         print("demo_1_wrapper is over")
#
#     print("demo_2 is out")
#     return wrapper
#
#
# @demo_1
# @demo_2
# def fun(a, b):
#     print("hello fun, 调用fun函数")
#     time.sleep(1)
#     print("result is {}".format((a + b)))


#  高阶：不带参数的类装饰器
# 基于类装饰器的实现，必须实现 __call__ 和 __init__两个内置函数。
# __init__ ：接收被装饰函数；
# __call__ ：实现装饰逻辑。
class Logger:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("[INFO]: the function {func}() is running..." \
              .format(func=self.func.__name__))
        return self.func(*args, **kwargs)


@Logger
def say(somthing):
    print("say {}！".format(somthing))


# 高阶：带参数的类装饰器
# 带参数和不带参数的类装饰器有很大的不同。
# __init__ ：不再接收被装饰函数，而是接收传入参数；
# __call__ ：接收被装饰函数，实现装饰逻辑。
class Logger2:
    def __init__(self, level="INFO"):
        self.level = level

    def __call__(self, func):  # 接受函数
        def wrapper(*args, **kwargs):
            print("[{level}]: the function {func}() is running...".format(level=self.level, func=func.__name__))
            func(*args, **kwargs)
        return wrapper  # 返回函数


@Logger2(level='WARNING')
def say2(somthing):
    print("say {}！".format(somthing))


if __name__ == '__main__':
    # fun()
    # print("f.__name__ is", fun.__name__)
    # fun_2(3, 4, 5)
    # print("---------------")
    # fun_1(3, 4)
    # fun(3, 4, 5)
    # value = fun(3, 6)
    # print("-----------------------")
    # print(value)
    # fun(3, 4)
    # say("hello")
    say2("python")
